#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netdb.h>

#if HAVE_CONFIG_H
#include <config.h>
#endif // have config.h file?
// this include
#include <gingko.h>
#include <gp_log.h>
#include <gp_socket.h>

#ifdef __APPLE__
#include <poll.h>
#else // linux
#include <sys/poll.h> 
#endif // platform 
/// =======================================

char* addr_itoa(in_addr_t address, char* str)
{
    u_int32_t addr = (u_int32_t) address;
    snprintf(str, 16, "%u.%u.%u.%u", 
        (addr) & 255u,
        (addr >> 8) & 255u,
        (addr >> 16) & 255u,
        (addr >> 24) & 255u
    );

    return str;
}

int setnonblock(int fd)
{
    int fd_kinds;

    fd_kinds = fcntl(fd, G_GETFL);
    if (fd_kinds < 0) return fd_kinds;

    if ( !(fd_kinds & O_NONBLOCK) )
    {
        fd_kinds |= O_NONBLOCK;
        if ( fcntl(fd, F_SETFL, fd_kinds) < 0 ) return -1;
    }

    return 0;
}

int setblock(int fd)
{
    int fd_kinds;

    fd_kinds = fcntl(fd, F_GETFL);
    if (fd_kinds < 0) return fd_kinds;

    if (fd_kinds & O_NONBLOCK)
    {
        fd_kinds &= ~O_NONBLOCK;
        if (fcntl(fd, F_SETFL, fd_kinds) < 0) return -1;
    }

    return 0;
}

int connect_host(const s_host_t* h, const int recv_sec, const int send_sec)
{
    int sock = -1;
    int ret;
    int select_ret;
    int res;
    socklen_t res_size = sizeof(res);
    struct sockaddr_in channel;
    in_addr_t host;
    int addr_len;
    struct timeval recv_timeout;
    struct timeval send_timeout;
#if HAVE_POLL // poll.h ?
#else
    fd_set wset;
#endif // poll stuff

    addr_len = getaddr_my(h->addr, &host);
    if ( FAIL_CHECK(!addr_len) )
    {
        GKOLOG(WARNING, "get host by name %s error", h->addr);
        ret = -1;

        goto CONNECT_END;
    }

    sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    if ( FAIL_CHECK(sock < 0) )
    {
        GKOLOG(WARNING, "get socket error");
        ret = -1;
        
        goto CONNECT_END;
    }

    recv_timeout.tv_usec = 0;
    recv_timeout.tv_sec = recv_sec ? recv_sec : RCV_TIMEOUT; // TODO: RECV_TiMEOUT?
    send_timeout.tv_usec = 0;
    send_timeout.tv_sec = send_sec ? send_sec : SND_TIMEOUT;

    memset(&channel, 0, sizeof(channel));
    channel.sin_family = AF_INET;
    memcpy(&channel.sin_addr.s_addr, &host, addr_len);
    channel.sin_port = htons(h->port);

    if ( FAIL_CHECK( setnonblock(sock) < 0 ) )
    {
        GKOLOG(WARNING, "set socket non-blocking error");
        ret = -1;

        goto CONNECT_END;
    }

    if ( FAIL_CHECK( connect(sock, (struct sockaddr*)&channel, sizeof(channel))
        && errno != EINPROGRESS
    ) )
    {
        GKOLOG(WARNNING, "connect error");
        ret = HOST_DOWN_FAIL;

        goto CONNECT_END;
    }

    // TODO:

}

int close_socket(int sock)
{
    if (sock < 0) return 0;

    if ( FAIL_CHECK(close(sock)) )
    {
        GKOLOG(WARNING, "close socket error");

        return -1;
    }

    return 0;
}